﻿using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Text;

namespace G.SL.Net
{

    public abstract class HttpRequest : IHttpRequest
    {
        /// <summary>
        /// The uri the request goes to.
        /// </summary>
        protected string Uri;

        /// <summary>
        /// Initializes a new instance of <see cref="HttpRequest"/> with the specified <paramref name="uri"/>.
        /// </summary>
        /// <param name="uri">The uri the request goes to.</param>
        protected HttpRequest(string uri)
        {
            this.Uri = uri;
        }

        private System.Collections.Generic.Dictionary<string, string> headers = new System.Collections.Generic.Dictionary<string, string>();
        public System.Collections.Generic.Dictionary<string, string> Headers
        {
            get
            {
                return headers;
            }
        }


        /// <summary>
        /// Performs the HTTP request.
        /// </summary>
        /// <returns>The server response string(UTF8 decoded).</returns>
        public virtual void Request(Action<WebException, string> callback)
        {
            HttpWebRequest req = HttpWebRequest.Create(ConstructUri()) as HttpWebRequest;

            foreach (var kv in Headers)
            {
                req.Headers[kv.Key] = kv.Value;
            }
            //AppendHeaders(req.Headers);
            if (!string.IsNullOrEmpty(Method))
            {
                req.Method = Method;
            }
            if (!string.IsNullOrEmpty(ContentType))
            {
                req.ContentType = ContentType;
            }

            PrepareRequest(req);

            if (req.Method == "POST")
            {
                req.BeginGetRequestStream((i) =>
                {
                    try
                    {
                        var responseStream = req.EndGetRequestStream(i);
                        WriteBody(responseStream);
                        responseStream.Close();
                    }
                    catch (WebException ex)
                    {
                        if (callback != null)
                            callback(ex, ex.Message);
                        else
                            throw ex;
                    }
                    req.BeginGetResponse((_i) =>
                    {
                        try
                        {
                            var response = req.EndGetResponse(_i);
                            callback(null, RetrieveResponse(response));
                        }
                        catch (WebException ex)
                        {
                            if (callback != null)
                                callback(ex, ex.Message);
                            else
                                throw ex;
                        }
                    }, null);
                }, null);

            }
            else if (req.Method == "GET")
            {
                req.BeginGetResponse((i) =>
                {
                    try
                    {
                        var response = req.EndGetResponse(i);
                        callback(null, RetrieveResponse(response));
                    }
                    catch (WebException ex)
                    {
                        if (callback != null)
                            callback(ex, ex.Message);
                        else
                            throw ex;
                    }
                }, null);
            }
        }
        /// <summary>
        /// When overriden in derived classes, constructs the final request uri based on relevent conditions.
        /// </summary>
        /// <returns>The final request uri constucted.</returns>
        protected virtual string ConstructUri()
        {
            return Uri;
        }

        /// <summary>
        /// When overridden in derived classes, sets additional request arguments on the web request object.
        /// </summary>
        /// <param name="webRequest">The web request object.</param>
        protected virtual void PrepareRequest(HttpWebRequest webRequest)
        {
            // Nothing to do.
        }

        /// <summary>
        /// When overridden in derived classes, writes data into the request stream.
        /// <remarks>HTTP GET type request is not supported.</remarks>
        /// </summary>
        /// <param name="reqStream">The request stream object.</param>
        protected virtual void WriteBody(Stream reqStream)
        {
            // Nothing to do. 
        }

        /// <summary>
        /// Retrieves the response from the request.
        /// </summary>
        /// <param name="webResponse"></param>
        /// <returns>The server response string(UTF8 decoded).</returns>
        protected virtual string RetrieveResponse(WebResponse webResponse)
        {
            var respContent = string.Empty;
            var respStream = webResponse.GetResponseStream();
            using (StreamReader reader = new StreamReader(respStream, Encoding.UTF8))
            {
                respContent = reader.ReadToEnd();
            }
            webResponse.Close();

            return respContent;
        }

        /// <summary>
        /// Gets or sets the HTTP method of the request.(i.e: POST, GET etc.)
        /// </summary>
        protected virtual string Method
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the content type of the request.(i.e: text/plain etc.)
        /// </summary>
        public string ContentType
        {
            get;
            set;
        }
    }
}
